Skip to content

refactor(domain): tighten the object calisthenics story - #7

Merged
arnaud-zg merged 1 commit into
fix/domain-red-suite-and-dead-repofrom
refactor/object-calisthenics-domain
Sep 2, 2026
Merged

arnaud-zg merged 1 commit into
fix/domain-red-suite-and-dead-repofrom
refactor/object-calisthenics-domain

Conversation

@arnaud-zg

Copy link
Copy Markdown
Owner

Stacked on #6. This is the one the article is actually about.

Summary

  • CartItems: a first-class collection absorbing the find/map/filter/reduce that was
    duplicated three times in Cart, replacing getItemsCopy()
  • Cart now takes item ids (increaseQuantity, decreaseQuantity, removeItem), so the
    UI no longer pulls a Product back out of a CartItem just to hand it back to Cart.
    Added remainingForFreeShipping(), the Money arithmetic that used to live in JSX
  • Dropped CartItem.getProduct() (the source of that chain) and the dead getPrice(),
    renamed the rest to id()/name()/image()/quantity()
  • Quantity clamping moved off CartItem onto Quantity itself
  • Skill's vocabulary and Product's knowledge tiers were the same concept with two
    different words (intermediate vs adventurer), bridged by a ProductCardLogic shim.
    Unified on one type, deleted the shim
  • GoldSilverCopperFormatter split into conversion and presentation, both got the specs
    they never had
  • Stats.asObject() dissolved the value object into a plain record the UI had to
    re-derive labels from, and it rendered ManaBoost: 0 rows. Stats.list() now returns
    labeled, non-zero entries directly
  • A product's lore link and video never actually varied by knowledge level, only the
    data entry did. Moved that onto Product itself
  • data/products.ts is now a ProductCatalog built from named blueprints instead of a
    bare array of five-deep positional constructor calls
  • Moved the three WelcomeSurvey storage repositories out of a UI component folder into
    domain/welcomeSurvey/infrastructure/
  • Shared test fixtures replace the ~90 lines of product construction duplicated across
    two spec files
flowchart LR
  A[Cart: 90 lines, 3x duplicated find] --> B[Cart: 58 lines + CartItems collection]
  C[getProduct Demeter chain] --> D[Cart.increaseQuantity by id]
  E[Skill vs KnowledgeProfile] --> F[one shared vocabulary]
Loading

Cart went from 90 lines to 58, CartItem from 75 to 51: close to the 50-line rule, not
exactly on it.

Test plan

  • pnpm typecheck
  • pnpm check
  • pnpm build
  • pnpm test, 14 files, 89 tests, all green

🤖 Generated with Claude Code

https://claude.ai/code/session_014BcGPRc3sgUJYKihGRXh5g

- CartItems: a first-class collection absorbing the find/map/filter/reduce that used to
  be duplicated three times in Cart, replacing getItemsCopy()
- Cart now takes item ids (increaseQuantity, decreaseQuantity, removeItem), so the UI no
  longer pulls a Product back out of a CartItem just to hand it back to Cart. Added
  remainingForFreeShipping(), the Money arithmetic that used to live in JSX
- dropped CartItem.getProduct() (the source of that Demeter chain) and the dead
  getPrice(); renamed the rest to id()/name()/image()/quantity() to match the rest of
  the domain's naming
- quantity clamping moved off CartItem onto Quantity (clampBetween, isAtLeast, isAtMost),
  Money got isAtLeast/isZero so policies stop comparing raw .toAmount() values
- Skill's SkillValue and Product's KnowledgeProfile were the same concept with two
  vocabularies (intermediate vs adventurer) bridged by a ProductCardLogic shim. Unified
  on one type, deleted the shim
- GoldSilverCopperFormatter split into GoldSilverCopperAmount (conversion) and
  GoldSilverCopperFormatter (presentation), both got the specs they never had
- Stats.asObject() dissolved the value object into a plain record the UI had to
  re-derive labels from (and rendered "ManaBoost: 0" rows). Stats.list() now returns
  labeled, non-zero entries directly
- a product's lore link and video never actually varied by knowledge level, only the
  data entry did (same two URLs repeated three times per product). Moved ExtraResources
  onto Product itself
- data/products.ts is now a ProductCatalog built from named blueprints instead of a bare
  array of five-deep positional constructor calls
- moved the three WelcomeSurvey storage repositories out of a UI component folder into
  domain/welcomeSurvey/infrastructure/
- shared test fixtures (productFixtures.ts) replace the ~90 lines of product
  construction duplicated across Cart.spec.ts and CartItem.spec.ts

Cart went from 90 lines to 58, CartItem from 75 to 51: close to the 50-line rule, not
exactly on it, and a first-class collection exists now where none did before.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014BcGPRc3sgUJYKihGRXh5g
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant